/*******************************************************************************
  * 文件：RunProtect.c
  * 作者：yk
  * 版本：v1.0.0
  * 日期：2024-03-20
  * 说明：炉头运行保护,依据条件限制运行参数
*******************************************************************************/
/* 头文件 *********************************************************************/
#include "HobCtrl.h"
#include "DevRun.h"
#include "DevState.h"
#include "DevStandby.h"
#include "Debug.h"
#include "Hardware_IO.h"
#include "Display.h"
#include "YLDeviceCtrl.h"
#include "RunProtect.h"

/* 宏定义 *********************************************************************/
/* 类型定义 *******************************************************************/
typedef enum
{
    eERR_NONE = 0,      // No Error
    eERR_E1 = 0xE1,        // Over Voltage
    eERR_E2 = 0xE2,        // Under Voltage
    eERR_E3 = 0xE3,        // Pot Hight Temp
    eERR_E5 = 0xE5,        // IGBT Hight Temp
    eERR_F3 = 0xF3,        // Pot NTC Short
    eERR_F4 = 0xF4,        // Pot NTC Open
    eERR_F6 = 0xF6,        // Comm Failure
    eERR_F9 = 0xF9,        // IGBT NTC Short
    eERR_FA = 0xFA,        // IGBT NTC Open 
}ErrorCode_te;

/* 变量定义 *******************************************************************/
extern DevRunCtrl_ts sDevRunCtrl;
RunProtectCtrl_ts sRunProtectCtrl;

/* 函数声明 *******************************************************************/
/* 函数定义 *******************************************************************/


static void DevProtect_ShowErrorCode(U8 u8CZIndex, ErrorCode_te eErrorCode)
{   
    // 定义是否要显示F前缀
    Bool bShowF = TRUE;

    // 确认第二位数字是否为A - 先不考虑数字为A的情况
    // if( ((U8)eErrorCode | 0x0F) != 0x0A)
    // {
    //     U8 u8ErrNum = ( (U8)eErrorCode | 0x0F );
    // }
    // else
    // {
    //     U8 u8ErrNum = 0x0A;
    // }

    if((eErrorCode >> 5) == 0x0E)
    {
        bShowF = FALSE;
    }

    if(OS_TimerGetExpiredTimes(&sRunProtectCtrl.asProtectScanTimer)%2)
    {   
        // 先判断是否显示 F 或 E
        switch (u8CZIndex)
        {
            case u8CZ1:
                if(bShowF)
                {    Display_Hob1Show(DISPLAY_Index_F);    }
                else
                {    Display_Hob1Show(DISPLAY_Index_E);    }
                break;
            case u8CZ2:
                if(bShowF)
                {    Display_Hob2Show(DISPLAY_Index_F);    }
                else
                {    Display_Hob2Show(DISPLAY_Index_E);    }
                break;
            case u8CZ3:
                if(bShowF)
                {    Display_Hob3Show(DISPLAY_Index_F);    }
                else
                {    Display_Hob3Show(DISPLAY_Index_E);    }
                break;
            case u8CZ4:
                if(bShowF)
                {    Display_Hob4Show(DISPLAY_Index_F);    }
                else
                {    Display_Hob4Show(DISPLAY_Index_E);    }
                break;
            default:
                break;
        }
    }
    // 显示错误码中的数字
    else
    {
        switch (u8CZIndex)
        {
            case u8CZ1:
                Display_Hob1Show( ((U8)eErrorCode & 0x0F) );
                break;
            case u8CZ2:
                Display_Hob2Show( ((U8)eErrorCode & 0x0F) );
                break;
            case u8CZ3:
                Display_Hob3Show( ((U8)eErrorCode & 0x0F) );
                break;
            case u8CZ4:
                Display_Hob4Show( ((U8)eErrorCode & 0x0F) );
                break;
            default:
                break;
        }
    }
}

/*******************************************************************************
  * 函数名：DevProtect_SetCommFailFlag
  * 功  能：设备保护-设置通讯故障标志位
  * 参  数：U8 u8CZIndex - 炉头索引，Bool bFlag - 通讯故障标志位
  * 返回值：无
  * 说  明：
*******************************************************************************/
void DevProtect_SetCommFailFlag(U8 u8CZIndex, Bool bFlag)
{   
    // 设置指定炉头的通讯故障标志位为指定值
    sRunProtectCtrl.bCommFailFlag[u8CZIndex] = bFlag;
}

Bool DevProtect_GetCommFailFlag(U8 u8CZIndex)
{   
    // 设置指定炉头的通讯故障标志位为指定值
    return sRunProtectCtrl.bCommFailFlag[u8CZIndex];
}

// 监测是否出现通信故障
// Bool DevProtect_CheckCommFail(void)
// {
//     return sRunProtectCtrl.bCommFailFlag[u8CZ1] || sRunProtectCtrl.bCommFailFlag[u8CZ2] || 
//            sRunProtectCtrl.bCommFailFlag[u8CZ3] || sRunProtectCtrl.bCommFailFlag[u8CZ4];
// }

/*******************************************************************************
  * 函数名：DevProtect_CommCheckHandler
  * 功  能：设备保护-通讯检测处理
  * 参  数：U8 u8CZIndex - 炉头索引
  * 返回值：无
  * 说  明：每500ms执行一次;内部执行：处理与通信故障相关的操作，包括显示故障码、暂停加热等
*******************************************************************************/
void DevProtect_CommCheckHandler(U8 u8CZIndex)
{
    // 通讯故障检测
    if(sRunProtectCtrl.au8CommCheckTick[u8CZIndex] > 0)
    {
        // 将对应的通讯故障标志位置FALSE
        DevProtect_SetCommFailFlag(u8CZIndex, FALSE);

        sRunProtectCtrl.au8CommCheckTick[u8CZIndex]--;
    }
    // 当计时清为零后，判定为出现通信故障。
    else
    {   
        // 调用对故障码的显示 - F6
        DevProtect_ShowErrorCode(u8CZIndex, eERR_F6);

        // 将对应的通讯故障标志位置TRUE
        DevProtect_SetCommFailFlag(u8CZIndex, TRUE);
    } 

    // 本次通讯故障标志位与上次不同时，说明通讯故障标志位发生变化，进行更新处理(恢复正常 或 进入故障状态)
    if(sRunProtectCtrl.bCommFailFlag[u8CZIndex] != sRunProtectCtrl.bCommFailFlagLast[u8CZIndex])
    {   
        // 通讯故障标志位改变为FALSE时，恢复加热
        if(sRunProtectCtrl.bCommFailFlag[u8CZIndex] == FALSE)
        {
            // 信息调试
            DEBUG("Hob-%d Resume\r\n", u8CZIndex);
            
            // 恢复加热
            HobCtrl_ResumeHeating(u8CZIndex);
        }
        else
        {
            // 信息调试
            DEBUG("Hob-%d Pause\r\n", u8CZIndex);

            // 暂停加热
            HobCtrl_PauseHeating(u8CZIndex);
        }

        // 更新上次通讯故障标志位
        sRunProtectCtrl.bCommFailFlagLast[u8CZIndex] = sRunProtectCtrl.bCommFailFlag[u8CZIndex];
    }
}

/*******************************************************************************
  * 函数名：DevProtect_ProtectScanHook
  * 功  能：设备保护-保护检测回调函数
  * 参  数：无
  * 返回值：无
  * 说  明：供asProtectScanTimer定时器调用，每500ms执行一次;内部执行：
  *        1. IGBT温度检测
  *        2. 通讯故障检测
*******************************************************************************/
static void DevProtect_ProtectScanHook(void)
{
    // 遍历四个炉头
    for(U8 u8CZIndex = u8CZ1;u8CZIndex <= u8CZ4;u8CZIndex++)
    {
        // 通讯故障检测  - 优先级最高
//        DevProtect_CommCheckHandler(u8CZIndex);
        
        // IGBT温度检测
        if(sRunProtectCtrl.au8IGBTTempCheckTick[u8CZIndex] > 0)
        {
            sRunProtectCtrl.au8IGBTTempCheckTick[u8CZIndex]--;
        }
        else
        {
            // 获取指定炉头IGBT温度(临时变量)
            U8 u8IGBTTemp = YLComm_IGBTTempGet(u8CZIndex);
            
            // // 初始化IGBT温度级别(临时变量)
            // U8 u8IGBTTempLevel = 0;

            // 判断IGBT温度状态 - 级别越低，温度越高
            if(u8IGBTTemp <= HEAT_TIGBT_95)
            {   sRunProtectCtrl.au8IGBTTempLevel[u8CZIndex] = 1;    }
            else if(u8IGBTTemp <= HEAT_TIGBT_85)
            {   sRunProtectCtrl.au8IGBTTempLevel[u8CZIndex] = 2;    }
            else if(u8IGBTTemp <= HEAT_TIGBT_80)
            {   sRunProtectCtrl.au8IGBTTempLevel[u8CZIndex] = 3;    }
            else if(u8IGBTTemp <= HEAT_TIGBT_75)
            {   sRunProtectCtrl.au8IGBTTempLevel[u8CZIndex] = 4;    }
            else
            {   sRunProtectCtrl.au8IGBTTempLevel[u8CZIndex] = 4;    }


            // 用四号炉头进行调试
            if(u8CZIndex == u8CZ4)
            {
                // 信息调试
                DEBUG("Hob-%d\r\n", sRunProtectCtrl.au8IGBTTempLevel[u8CZIndex]);

                // 信息调试
                DEBUG("Hob - %d\r\n", YLComm_IGBTTempGet(u8CZ4));

                // 保护处理
                // DevStandby_StopHeating(u8CZIndex);
            }

            // IGBT温度检测
            sRunProtectCtrl.au8IGBTTempCheckTick[u8CZIndex] = HEAT_IGBT_CHECK_TICK;
        }
    

    }

    // 对定时器清零操作，防止定时器次数溢出 - 512 * 0.5s = 256s
    if(OS_TimerGetExpiredTimes(&sRunProtectCtrl.asProtectScanTimer) == 512)
    {
        OS_TimerClearExpiredTimes(&sRunProtectCtrl.asProtectScanTimer);
    }
}

/*******************************************************************************
  * 函数名：DevProtect_SetCommCheckTick
  * 功  能：设备保护-重置通讯检测的倒计时
  * 参  数：U8 u8CZIndex - 炉头索引
  * 返回值：无
  * 说  明：供外部调用，当通讯成功时则将检测计时置为预设值
*******************************************************************************/
void DevProtect_SetCommCheckTick(U8 u8CZIndex)
{   
    sRunProtectCtrl.au8CommCheckTick[u8CZIndex] = Comm_CHECK_TICK;
}

void DevProtect_SetPower(U8 u8CZIndex,U8 u8Power)
{   
    U16 u16TempPower = u8Power;

    // 依据IGBT温度等级进行控制(按比例设定功率)
	switch(sRunProtectCtrl.au8IGBTTempLevel[u8CZIndex])
	{
		case 1:
			u16TempPower = (U16)u8Power;
			u16TempPower = u16TempPower*20;
			u16TempPower = u16TempPower/100;
		break;
        case 2:
			u16TempPower = (U16)u8Power;
			u16TempPower = u16TempPower*40;
			u16TempPower = u16TempPower/100;
		break;
        case 3:
			u16TempPower = (U16)u8Power;
			u16TempPower = u16TempPower*60;
			u16TempPower = u16TempPower/100;
		break;
        case 4:
			u16TempPower = (U16)u8Power;
			u16TempPower = u16TempPower*100;
			u16TempPower = u16TempPower/100;
		break;
		default:
		break;
    }
    


    // 调用基于通信部分的功率下发
    YLComm_CookZoneCtrl(u8CZIndex, (U8)u16TempPower);
}

// 初始化
void DevProtect_Init(void)
{
    // 遍历四个炉头
    for(U8 u8CZIndex = u8CZ1;u8CZIndex <= u8CZ4;u8CZIndex++)
    {
        // 初始化IGBT温度检测倒计时
        sRunProtectCtrl.au8IGBTTempCheckTick[u8CZIndex] = HEAT_IGBT_CHECK_TICK;
        
        // 初始化通讯故障检测参数
        sRunProtectCtrl.au8CommCheckTick[u8CZIndex] = Comm_CHECK_TICK;
        sRunProtectCtrl.bCommFailFlag[u8CZIndex] = FALSE;
    }

    // 初始化保护扫描定时器 - 500ms
    OS_TimerRestart(&sRunProtectCtrl.asProtectScanTimer, 500, DevProtect_ProtectScanHook);
}

/***************************** 文件结束 ***************************************/

